#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/signal.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/device.h>
#include <linux/pci.h>
#include <linux/interrupt.h> 
#include <asm/uaccess.h> 
 
#include <linux/jiffies.h>
#include <linux/semaphore.h>

#include "ioctl_drv_usr.h"

extern void pcie_bar0_set(int offset, unsigned int val);
extern unsigned int pcie_bar0_get(int offset);



unsigned long virt_addr_kern[BLK_CNT_4M] = {0};
unsigned long phys_addr_kern[BLK_CNT_4M] = {0};

static int gBlk_4M_cnt = 0;

unsigned long  virt_addr_kern_get(int idx)
{
	return virt_addr_kern[idx];
}

unsigned long  phys_addr_kern_get(int idx)
{
	return phys_addr_kern[idx];
}

void iounmap_all(void)
{
	int i = 0;
	
	for (i=0; i<BLK_CNT_4M; i++)
	{
		if (virt_addr_kern[i] != 0)
		{
			iounmap((void *)(virt_addr_kern[i]));
		}
	}

	return;
}

void kmalloc_dma_init_all(void)
{
	int malloc_size = SIZE_4M;   // 4M - only 4M is ok

	int i = 0;
	unsigned long virt_addr, phys_addr;
	unsigned long delta = 0;
	
	for (i=0; i<BLK_CNT_4M; i++)
	{	
		//
		//  kmalloc 4M ddr blcok of BLK_CNT_4M
		//
		virt_addr = (unsigned long)kmalloc(malloc_size, GFP_KERNEL); 
		if (virt_addr != 0)
		{
			phys_addr = virt_to_phys(virt_addr);	

			virt_addr_kern[i] = virt_addr;
			phys_addr_kern[i] = phys_addr;
			
			gBlk_4M_cnt++;

			if (i > 0)
			{
				delta = phys_addr_kern[i-1] - phys_addr_kern[i];  // 地址递减
			}			
			printk("KERNEL phys_all<%d>: 0x%0lX(%dM) - (v)0x%0lX, [0x%lX]-<%d> \n", \
					i, phys_addr, (malloc_size/SIZE_1M), virt_addr, delta, gBlk_4M_cnt);
		}
		else
		{
			printk("kmalloc fail: (%d)-0x%lX \n", i, (i*SIZE_4M));
			return;
		}
	}	

	return;
}

unsigned long offset_to_phys_all(unsigned int offset)
{
	unsigned int idx = offset / SIZE_4M;  // Integer number
	unsigned int rem = offset % SIZE_4M;  // remainder; complement of a number;
	unsigned long ret = 0;
	
	ret = phys_addr_kern[idx] + rem;
	//printk("ret-all: 0x%X-0x%lX", ret, idx);
	
	return ret;
}

unsigned long offset_to_virt_all(unsigned int offset)
{
	unsigned int idx = offset / SIZE_4M;  // Integer number
	unsigned int rem = offset % SIZE_4M;  // remainder; complement of a number;
	unsigned long ret = 0;
	
	ret = virt_addr_kern[idx] + rem;
	//printk("ret-all: 0x%X-0x%lX", ret, idx);
	
	return ret;
}

int fpga_share_mem_mmap_all(struct vm_area_struct *vma)
{
	int ret = 0, i = 0;	
	unsigned long length = 0;
	unsigned long area_4m_idx = 0; 
	
	length = vma->vm_end - vma->vm_start;	
	
	//vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);	//no cache
	vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
	
	for (i=0; i<BLK_CNT_4M; i++)
	{
		area_4m_idx = (unsigned long)((i * SIZE_4M) / PAGE_SIZE);

		if (area_4m_idx == vma->vm_pgoff)
		{
			ret = remap_pfn_range(vma, vma->vm_start, (phys_addr_kern[i] >> PAGE_SHIFT),  \
								  length, vma->vm_page_prot);
			if (ret < 0) 
			{	 
				printk("area_4m_idx_(%d) fpga remap_pfn_range() fail... \n\r", i);
				return -ENOBUFS;
			}
			else
			{	 
				printk("area_4m_idx_(%d) fpga remap_pfn_range() ok! \n\r", i);
				return 0;	
			}
		}
	}
}




#if 1
void kmalloc_dma_rx_start_all(void)
{
#ifdef CHN1_OPEN
	kmalloc_dma_rx_start_chn_1();
#endif

#ifdef CHN2_OPEN
	kmalloc_dma_rx_start_chn_2();
#endif

#ifdef CHN3_OPEN
	kmalloc_dma_rx_start_chn_3();
#endif

#ifdef CHN4_OPEN
	kmalloc_dma_rx_start_chn_4();
#endif

#ifdef CHN5_OPEN
	kmalloc_dma_rx_start_chn_5();
#endif

#ifdef CHN6_OPEN
	kmalloc_dma_rx_start_chn_6();
#endif

#ifdef CHN7_OPEN
	kmalloc_dma_rx_start_chn_7();
#endif

	return;
}
#endif


/*
6、--------------------------------新增中断时间测试------------------------
1.寄存器
FPGA_INT_TRIG(0x800)
cpu写该寄存器为1，触发FPGA发送总断到CPU

FPGA_INT_TRIG_WR_TIME(0X804)
cpu写FPGA_INT_TRIG时 记录一个起始时间点，32位计时值

FPGA_INT_CLR_TRIG(0x808)
cpu响应中断，清FPGA_INT_TRIG寄存器，写1自清0

FPGA_INT_TRIG_WR_TIME(0X80c)
cpu写FPGA_INT_CLR_TRIG 时，结束时间点，32位计时值
*/
#if 1
#define FPGA_INT_TRIG            (0x800)
#define FPGA_INT_TRIG_WR_TIME    (0x804)
#define FPGA_INT_CLR_TRIG        (0x808)
#define FPGA_INT_TRIG_CLR_TIME   (0x80C)

u32 g_irq_time = 0;;
void test_trig_pcie_irq(void)
{
	pcie_bar0_set(FPGA_INT_TRIG, 1);
	return;
}

void test_clr_trig_pcie_irq(void)
{
	pcie_bar0_set(FPGA_INT_CLR_TRIG, 1);
	return;
}

u32 test_rd_irq_start_time(void)
{
	return pcie_bar0_get(FPGA_INT_TRIG_WR_TIME);
}

u32 test_rd_irq_end_time(void)
{
	return pcie_bar0_get(FPGA_INT_TRIG_CLR_TIME);
}

u32 test_get_irq_time(void)
{
	return g_irq_time;
}
#endif


